#pragma once

/* wfspan层 */

#include <metalc/block/wfspan_obj.h>

OU2INFUN(cls, wfspan_clspol)


SAI void _wfslab_pinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	INFO("_wfslab pinit\n");
}

SAI void _wfslab_tinit(const mlfunc_t * cont, perm_allocf_t *alloc, wfspan_conf_t *conf){
	INFO("_wfslab tinit\n");
	mtls_t *tls = mtls_get();
	ASSERT(tls);
	// ASSERT(tls->objs[cont->lidx] == NULL_PTR);
	size_t sz = wfspanobj_size(conf->total_szcls, conf->l2_cls);
	void *mem = alloc(sz);
	ASSERT(mem);

	/* TODO: 解决重复初始化的问题 */
	if(!tls->objs[cont->lidx]) {
		tls->objs[cont->lidx] = wfspan_init(mem, sz, conf);
	}
	else{
		INFO("_wfslab tinit already %p\n", tls->objs[cont->lidx]);
	}
}




/* wfpage */

/* define wfpage */

/* wfslab(预留wfpage接口) */

/* define wfslab */

/* wfspan = wfslab+wfpage*/

/* define wfspan */


SAI void *wfslab_oomhandler(mreq_t *req, size_t size, const mlfunc_t * cont, wfspan_conf_t *conf){
	void *ret;

	/* 一次申请更多? 或者加强cache? */

	req->size = size;
	req->align = req->align < conf->mslab_size ? conf->mslab_size : req->align;
	ret = cont->lalloc(req);
	INFO("[wfspan] oom alloc from upper %p, size %lx\n", ret, req->size);

	return ret;
}

SAI bool_t wfslab_try_alloc(mslab_t *cur, void **ptmp, size_t slab_size, hword_t unit_size)
{
	/* 由于wfspan空的都被抛弃，尝试申请的一定只有阻塞和成功获得内存块两种结果 */
	void *tmp_ret = mslab_try_alloc(cur, slab_size, unit_size);
	if (tmp_ret == SLAB_FLIST_BLCOKED){
		return FALSE;
	}
	else{
		*ptmp = tmp_ret;
		return TRUE;
	}
}

SAI void *wfslab_local_init_alloc(wfspan_obj_t *l, void *slab_ptr, size_t slab_size, wfspan_cls_t *szcls_arr, szcls_t cls, hword_t unit_size, hword_t max_nr){
	void *ret;	
	mslab_t *cur_slab = (mslab_t *)slab_ptr;
	__dlist_insert(&szcls_arr[cls].local_list, &cur_slab->node, &cur_slab->node, szcls_arr[cls].local_list.next);
	cur_slab = mslab_init(slab_ptr, l, slab_size, unit_size, max_nr, cls);
	ASSERT(cur_slab->cls == cls);
	ASSERT(cur_slab->flist == MSLAB_OFFST_NEXT);
	/* TODO: 不会被阻塞, 应该修改 */
	// ret = mslab_try_alloc(cur_slab, slab_size, unit_size);
	ret = cur_slab->bump_ptr;
	cur_slab->bump_ptr = (void *)((paddr_t)cur_slab->bump_ptr + unit_size);
	cur_slab->consumed_cnt -= 1;

	/* TODO: 只消耗一个也不会到达边界 */

	/* TODO: 不需要抛弃， 除非一个cur_slab数量等于1 */
	ASSERT(max_nr != 1);
	INFO("[wfspan] local alloc from full list, slab %p\n", cur_slab);
	ASSERT(ret);
	return ret;
}
SAI void *wfslab_local_alloc(wfspan_obj_t *l, wfspan_cls_t *szcls_arr, szcls_t cls, hword_t unit_size, wfspan_conf_t *conf){
	mslab_t *cur_slab = NULL_PTR;
	void *ret;
	while (!list_is_empty(&szcls_arr[cls].local_list)){
		/* 非空，从本地获得 */
		INFO("[wfspan] local alloc from cls %u\n", cls);
		dlist_t *cur_node = szcls_arr[cls].local_list.next;
		dlist_t *next_node = atm_ld(&cur_node->next);
		// ASSERT(next_node != NULL_PTR);
		dlist_t *prev_node = &szcls_arr[cls].local_list;
		cur_slab = list_entry(mslab_t, cur_node, node);
		ASSERT(cur_slab->cls == cls);
		if (wfslab_try_alloc(cur_slab, &ret, conf->mslab_size, unit_size)){
			/* 成功获得，先尝试discard */
			INFO("[wfspan] local alloc succ %p in [%p]\n", ret, cur_slab);
			if (mslab_discard_ifempty(cur_slab, l))
			{
				// 出队
				INFO("[wfspan] local discarded %p\n", cur_slab);
				dlist_del(prev_node, next_node);
				l->slab_nr--;
			}
			return ret;
		}
		else{
			/* 从本地获得失败(被阻塞) */
			INFO("[wfspan] local alloc blocked!!!! inseted %p to tail\n", cur_node);
			/* 删除后插到队列尾部 */
			dlist_del(prev_node, next_node);

#ifdef WFSPAN_BLOCKED_INSERT_TO_SPMC
			cur_slab->allocator = NULL_PTR;
			l->slab_nr--;
			wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
#else
			__dlist_insert(szcls_arr[cls].local_list.prev, cur_node, cur_node, &szcls_arr[cls].local_list);
#endif

		}
	}
	return NULL_PTR;
}
SAI void *wfslab_local_full_alloc(wfspan_obj_t *l, wfspan_cls_t *szcls_arr, szcls_t cls, hword_t unit_size, hword_t max_nr, wfspan_conf_t *conf){
	void *ret = NULL_PTR;
	if (!list_is_empty(&l->full_local_list)){
		/* full_list非空 */
		dlist_t *cur_node = l->full_local_list.next;
		// ASSERT(l->full_nr >= 1 && l->full_nr < conf->slab_limits);
		l->full_nr -= 1;
		l->slab_nr -= 1; /* FIXME: 也减1 */
		dlist_del(&l->full_local_list, cur_node->next);

		mslab_t *cur_slab = list_entry(mslab_t, cur_node, node);
		ret = wfslab_local_init_alloc(l, cur_slab, conf->mslab_size, szcls_arr, cls, unit_size, max_nr);
	}
	return ret;
}
SAI void *wfslab_acquire_from_others(wfspan_obj_t *l, wfspan_cls_t *szcls_arr, szcls_t cls, size_t unit_size, wfspan_conf_t *conf){
	INFO("[wfspan] try stealing cls spmc %u\n", cls);
	void *ret = NULL_PTR;

	/* 先尝试从spmc队列中取，取失败了尝试窃取 */
	mslab_t *cur_slab = wfslab_try_b4stealing(l, cls, conf);
	if (!cur_slab) cur_slab = wfspan_waitfree_acquire(l, cls, conf);


	if (LIKELY(cur_slab)){
		INFO("[wfspan] steal %p from %u\n", cur_slab, cls);
		/* 窃取成功 */
		/* TODO: 该赋值似乎不需要 */
		/* 尝试在该span分配 */
		ASSERT(cur_slab->allocator == NULL);
		if (wfslab_try_alloc(cur_slab, &ret, conf->mslab_size, unit_size)){	
			/* 分配成功 */
			INFO("[wfspan] try alloc %p from stealed %p\n", ret, cur_slab);
			if (!mslab_discard_ifempty(cur_slab, l)){
				/* 不用抛弃 */
				INFO("[wfspan] insert stealed slab %p to local cls %u head\n", cur_slab, cls);
				l->slab_nr++;
				/* 插入头部 */
				__dlist_insert(&szcls_arr[cls].local_list, &cur_slab->node, &cur_slab->node, szcls_arr[cls].local_list.next);
				ASSERT(cur_slab->cls == cls);
				cur_slab->allocator = l;
			}
		}
		else{
			INFO("[wfspan] insert stealed slab %p to spmc cls %u tail\n", cur_slab, cls);
			/* TODO: 塞回去还是插入到队列尾部 */
// #ifdef WFSPAN_BLOCKED_INSERT_TO_SPMC
// 			wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
// #else
// 			cur_slab->allocator = l;
// 			/* 插入头部或者尾部都可以，因为尾部和头部都为空 */
// 			__dlist_insert(szcls_arr[cls].local_list.prev, &cur_slab->node, &cur_slab->node, &szcls_arr[cls].local_list);
// 			ASSERT(cur_slab->cls == cls);
// 			l->slab_nr++;
// #endif
			wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
		}
	}
	return ret;
}
SAI void *_wfslab_alloc(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, INFUN(wfspan_clspol, clspol)){
	size_t req_size = req->size;
	/* 自对齐方式 */
	if (UNLIKELY(req->align != 0))
		req_size = align_up(req_size, req->align);
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, req_size)) return cont->lalloc(req);


	void *ret;
	void *upper_mem;
	mtls_t *tls = mtls_by_req(req);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);

	ASSERT(l);
	mslab_t *cur_slab = NULL_PTR;
	szcls_t cls = clspol->sz2cls(req_size);
	hword_t unit_size = (hword_t)clspol->cls2sz(cls);
	hword_t max_nr = u2mnr(cls);
	INFO("[wfspan] try alloc, need size %lx, utsz %x, cls %u\n", req_size, unit_size, cls);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);
	
	/* 先尝试本地申请 */
	ret = wfslab_local_alloc(l, szclsp, cls, unit_size, conf);
	if (LIKELY(ret)) goto FIN; 

	/* 本地申请失败，尝试窃取 */
	ret = wfslab_acquire_from_others(l, szclsp, cls, unit_size, conf);
	if (LIKELY(ret)) goto FIN;

	/* 优先窃取的话有助于减少碎片率，但对性能影响呢？ */
	/* 尝试从full_list中获取 */
	ret = wfslab_local_full_alloc(l, szclsp, cls, unit_size, max_nr, conf);
	if (LIKELY(ret)) goto FIN;

	/* 窃取失败，继续向上申请 */
	upper_mem = wfslab_oomhandler(req, conf->mslab_size, cont, conf);
	if (LIKELY(upper_mem)){
		/* 申请成功 */
		// hword_t max_nr = u2mnr(cls);
		l->slab_nr++;
		ret = wfslab_local_init_alloc(l, upper_mem, conf->mslab_size, szclsp, cls, unit_size, max_nr);
	
	}
FIN:	
	INFO("[wfspan] " PRIN_RED() "alloc " PRIN_DFT() " %p, need size %lx, real size %x, cls %u\n", ret, req_size, unit_size, cls);
	req->size = (size_t)unit_size;
	req->ptr = (void *)ret;
	req->nr = 1;
	return ret;
}
SAI dlist_t *wfslab_local_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, size_t *free_slab_nr, wfspan_conf_t *conf){
	PARAM_USED(cls, free_slab_nr);
	dlist_t *ret = NULL_PTR;
	/* 本地释放路径 */
	sword_t consumed_cnt = mslab_local_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1);
	INFO("[wfspan] local free %p in [%p], cur local cnt %ld, max nr %ld\n", free_ptr, s, consumed_cnt, mslab_visible_nr(s));
			
	/* 本地释放应该保留? */
	/* 需要在本地释放路径判断全局大小吗? */
	if ((consumed_cnt >= 0) &&
		// atm_ld(&s->rlist.cnt_val) == slab_max_nr + consumed_cnt
		(mslab_visible_nr(s) + (sword_t)consumed_cnt == slab_max_nr)){
		/* 需要释放span */
		dlist_del(s->node.prev, s->node.next);
		
		
		/* 优先放到本地full_list的头部 */
		__dlist_insert(&l->full_local_list, &s->node, &s->node, l->full_local_list.next);
		l->full_nr += 1;
		l->slab_nr += 1; /* FIXME: 加1个 */
		PARAM_USED(conf);
		// ASSERT(l->full_nr >= 1 && l->full_nr <= conf->slab_limits && l->full_nr <= l->slab_nr);

	}
	return ret;
}
SAI dlist_t *wfslab_remote_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, size_t *free_slab_nr, wfspan_conf_t *conf){
	PARAM_USED(slab_max_nr);
	dlist_t *ret = NULL_PTR;
	/* 使用dlist出队一定要确保node是第一个成员 */
	ASSERT(OFFSETOF(mslab_t, node) == 0);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);
	size_t slab_nr = l->slab_nr;

	INFO("[wfspan] remote free %p in [%p]\n", free_ptr, s);
	/* 远程释放 */
	if (mslab_remote_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1) != 0)
		return ret;
	/* 等于0则负责回收 */

	INFO("[wfspan] reclaim [%p]\n", s);

	mlistnode_t *reclaimed_list = mlist_hd_exchg(&s->rlist, NULL);

	s->flist = reclaimed_list;
	ASSERT(s->cls == cls);
	ASSERT(s->allocator == NULL);
	// TODO: 最近最少使用的出队
	if (slab_nr >= conf->slab_limits)
	{
		/* 大于本地限制则出队 */

		/* 优先出队本地已满的 */
		if (!list_is_empty(&l->full_local_list)) {
			/* 出队已满的最后一个 */
/* 			dlist_t *cur_node = l->full_local_list.prev;
			dlist_del(cur_node->prev, &l->full_local_list);
			l->full_nr -= 1;
			slab_nr -= 1;
			ret = cur_node;
			INFO("[wfspan]  return recently unused full span[%p]\n", ret);
			*free_slab_nr = 1; */

			/* TODO: 尝试清空队列 */
			*free_slab_nr = l->full_nr;
			slab_nr -= l->full_nr;
			slab_nr -= l->full_nr; /* FIXME: 减2个 */
			l->full_nr = 0;
			ret = l->full_local_list.next;
			ret->prev = l->full_local_list.prev;
			dlist_init(&l->full_local_list); 
		}
		else {
			if (!list_is_empty(&szclsp[cls].local_list)) {
				/* 没有使用则取最尾部的节点 */
				// 取最尾部
				dlist_t *tail = szclsp[cls].local_list.prev;
				/* 移除节点 */
				dlist_del(tail->prev, &szclsp[cls].local_list);
				mslab_t *rep = list_entry(mslab_t, tail, node);
				ASSERT(rep->cls == cls);
				ASSERT(tail->next == &szclsp[cls].local_list);
				slab_nr -= 1;
				/* 设置为无主 */
				atm_st(&rep->allocator, NULL);
				INFO("[wfspan] insert recently unused [%p] to spmc list\n", rep);
				wfspan_researved_mslabn(l, cls, rep, rep);
			}
			else {
				INFO("[wfspan] insert reclaim [%p] to spmc list\n", s);
				/* 将回收的span出队 */
				wfspan_researved_mslabn(l, cls, s, s);
				return ret;
			}
		}
	}

	INFO("[wfspan] reclaim [%p] to local list\n", s);
	/* 设置所有者 */
	s->allocator = l;
	slab_nr += 1;

	/* TODO:??? 不会插入到full free list */
	__dlist_insert(szclsp[cls].local_list.prev, &s->node, &s->node, &szclsp[cls].local_list);
	ASSERT(s->cls == cls);
	// 插到头部优先使用
	// __mdlist_add(&s->node, &l->szcls[cls].dlist, l->szcls[cls].dlist.next);
	l->slab_nr = slab_nr;

	return ret;
}
/* TODO: 查找Allocator的时候也可以通过本地索引判断？这样也能减少Cache行的竞争 */


static inline bool_t wfslab_oofhandler(mreq_t *req, dlist_t *s, size_t nr, const mlfunc_t * cont, wfspan_conf_t *conf)
{
	INFO("[wfspan] oof free slab %p, ptr %p, size %lx\n", s, req->ptr, conf->mslab_size);
	req->ptr = s;
	req->sptr = s->prev;
	// s->prev->next = NULL_PTR;
	req->size = conf->mslab_size;
	req->nr = nr;
	return cont->lfree(req);
}
/* 优化方案1， max_nr和owner_tid编码 */
SAI bool_t _wfslab_free(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, INFUN(wfspan_clspol, clspol)){
	size_t get_size = req->size;
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, get_size)) return cont->lfree(req);
	
	mslab_t *s = mslab_byptr(req->ptr, conf->mslab_size);
	wfspan_obj_t *r = (wfspan_obj_t *)atm_ld(&s->allocator);


	// 
	PARAM_USED(clspol);
	szcls_t cls = req->cls;
	hword_t max_nr = u2mnr(cls);
	INFO("[wfspan] try free %p, size %lx, cls %u\n", req->ptr, req->size, cls);
	mtls_t *tls = mtls_by_req(req);
	/* cid_t tid = tls->apdstls.tinfo.tid; */
	ASSERT(req->nr == 1);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(l);
	
	ASSERT(s->cls == cls);

	/* 释放 */
	dlist_t *ret;
	size_t free_slab_nr = 0;
	if (r == l){
		/* 本地释放 */
		ret = wfslab_local_free(l, s, req->ptr, cls, (sword_t)max_nr, &free_slab_nr, conf);
	}else{
		ret = wfslab_remote_free(l, s, req->ptr, cls, (sword_t)max_nr, &free_slab_nr, conf);
	}
	/* 释放后需要向上层释放 */
	if (UNLIKELY(ret)) {
		ASSERT(free_slab_nr != 0);
		return wfslab_oofhandler(req, ret, free_slab_nr, cont, conf);
	}
	
	return TRUE;
}

SAI size_t _wfslab_sizeof(mreq_t *req, const mlfunc_t * cont, wfspan_conf_t *conf){
	ASSERT(req->ptr);
	if (MLAYER_IS_LASTLAYER(cont) || !is_ptr_aligned(req->ptr, conf->mslab_size)){
		/* 节省free cls计算的时间 */
		mslab_t *s = mslab_byptr(req->ptr, conf->mslab_size);
		word_t cls_size = s->cls_size;
		szcls_t cls = word_high(cls_size);
		hword_t unit_size = word_low(cls_size);
		INFO("[wfspan] sizeof %p, sz %x, cls %u\n", req->ptr, unit_size, cls);
		req->size = unit_size;
		req->cls = cls;
		/* WFSPAN_PREFETCH(req->ptr); */ /* 提前预取，因为插入队列一定需要访问 */
		return req->size;
	}
	/* 大的 */
	return cont->lsizeof(req);
}

/* 定义 
 * min_bit - 管理的最小bit位
 * max_bit - 管理的最大bit位
 * slab_size - slab大小
 * slab_limit - 线程持有的slab个数限制，推荐为41
 * p_frac - travel的分数，推荐为0，越大内存占用越高，性能越好
 * help_quota - 单次帮助数量，推荐为1
 * cls_comp - szcls策略
 * u2mfunc - unit_size到max_nr的映射函数
 * perm_alloc - 永久对象分配函数
*/
#define DEF_WFSLAB_MLAYER(name, min_bit, max_bit, slab_size, slab_limit, p_frac, help_quota, cls_comp, u2mfunc, perm_alloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit), 1)\
static const wfspan_conf_t name##_conf = { .mslab_size = slab_size, .slab_limits = slab_limit, .travel_frac = p_frac, \
.total_szcls = _clsnr_##cls_comp, .max_help_quota = help_quota, .max_sz = _maxsz_##cls_comp, .l2_cls = 0 }; \
SAI void name##_pinit(const mlfunc_t * cont){ return _wfslab_pinit(cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _wfslab_tinit(cont, perm_alloc, &name##_conf); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _wfslab_alloc(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp));\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _wfslab_free(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp));\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _wfslab_sizeof(req, cont, &name##_conf); }

/* 增加2级 */